﻿using Puppy.Data;
using Puppy.Uow;
using System.Threading.Tasks;

namespace Puppy.ActorDb
{
    public class ActorDatabaseProvider<TActorDbContext> :
        IActorDatabaseProvider<TActorDbContext>
        where TActorDbContext : ActorDbContext
    {
        public TActorDbContext DbContext { get; }

        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IConnectionStringResolver _connectionStringResolver;
        private readonly ActorDatabaseManager _actorDatabaseManager;

        public ActorDatabaseProvider(
            TActorDbContext dbContext,
            IUnitOfWorkManager unitOfWorkManager,
            IConnectionStringResolver connectionStringResolver,
            ActorDatabaseManager actorDatabaseManager)
        {
            DbContext = dbContext;
            _unitOfWorkManager = unitOfWorkManager;
            _connectionStringResolver = connectionStringResolver;
            _actorDatabaseManager = actorDatabaseManager;
        }

        public Task<TActorDbContext> GetDbContextAsync()
        {
            return Task.FromResult(DbContext);
        }

        public async Task<IActorDatabase> GetDatabaseAsync()
        {
            var unitOfWork = _unitOfWorkManager.Current;
            if (unitOfWork == null)
            {
                throw new PuppyException($"A {nameof(IActorDatabase)} instance can only be created inside a unit of work!");
            }

            var connectionString = await _connectionStringResolver.ResolveAsync<TActorDbContext>();
            var dbContextKey = $"{typeof(TActorDbContext).FullName}_{connectionString}";

            var databaseApi = unitOfWork.GetOrAddDatabaseApi(
                dbContextKey,
                () => new ActorDbDatabaseApi(
                    _actorDatabaseManager.Get(connectionString)
                ));

            return ((ActorDbDatabaseApi)databaseApi).Database;
        }
    }
}
